﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using TheGameClient.Webservices;
using Waaahsabi.Controls.DWM;
using Waaahsabi.Controls.FileSystem;
using Waaahsabi.Controls.TheGame;

namespace TheGameClient
{
	public partial class MainPage
	{
		private Desktop _desktop;

		public MainPage()
		{
			App.RegisterDynamicUI();
			// Required to initialize variables
			InitializeComponent();
			InitializeControls();
		}

		private void InitializeControls()
		{
			App.Client.HeartbeatReceived += ClientHeartbeatReceived;
			App.Client.TraceReceived += Client_TraceReceived;
			App.Client.TraceEndedReceived += Client_TraceEndedReceived;
			App.Client.ServerDisconnectReceived += Client_ServerDisconnectReceived;

			App.Client.EmailReceived += ClientEmailReceived;
			App.Client.BankTransactionReceived += ClientBankTransactionReceived;

			App.Client.GlobalChatMessageReceived += ClientChatMessageReceived;

			App.Client.ServerErrorReceived += ClientServerErrorReceived;

			chatControl.SendMessage += chatControl_SendMessage;
			chatControl.SpamDetected += chatControl_SpamDetected;
			chatControl.ChatMessages.Add(new ChatMessageItem
			{
				MessageSender = "",
				MessageText =
					"Welcome to TheGame. This is the global chat. You can only read messages, but not post.\nFor user chat, connect to a system on the map, for instance your corporation's Internal Services server.",
				MessageTimestamp = DateTime.Now.ToShortTimeString()
			});

			chatControl.MouseEnter += delegate { VisualStateManager.GoToState(this, "ChatExpanded", true); };
			chatControl.MouseLeave += delegate { VisualStateManager.GoToState(this, "ChatCollapsed", true); };
			chatControl.ToggleInput(false);

			_desktop = new Desktop(cnvDesktop, taskBar);
			taskBar.StartMenuProgramExecuted += TaskBarStartMenuProgramExecuted;
			taskBar.ClientIP = App.MyIP;
			InitializeStartMenu();
		}

		private void ClientServerErrorReceived(object sender, ServerErrorReceivedEventArgs e)
		{
			switch (e.Severity)
			{
			case ServerErrorSeverity.Fatal:
				MessageBox.Show(string.Format("{0}: {1}", e.Severity, e.Message));
				break;
			default:
				chatControl.ChatMessages.Add(new ChatMessageItem
				{
					MessageSender = "Server",
					MessageText = string.Format("{0}: {1}", e.Severity, e.Message),
					MessageTimestamp = DateTime.Now.ToShortTimeString()
				});
				VisualStateManager.GoToState(this, "ChatExpanded", true);
				break;
			}
		}

		private void ClientBankTransactionReceived(object sender, BankTransactionReceivedEventArgs e)
		{
			chatControl.ChatMessages.Add(
				new ChatMessageItem
				{
					MessageTimestamp = DateTime.Now.ToShortTimeString(),
					MessageSender = "Banking System",
					MessageText =
						string.Format(
							e.Amount < 0
								? "{0} credits sent to {1} and deducted from Bank account {2} at {3}."
								: "{0} credits received from {2} and credited to bank account {1} at {3}.", e.Amount, e.ReceivingAccount,
							e.SendingAccount, e.transactionDate),
				});
			VisualStateManager.GoToState(this, "ChatExpanded", true);
		}

		private void ClientHeartbeatReceived(object sender, HeartbeatReceivedEventArgs e)
		{
			try
			{
				App.WorldTime = e.WorldTime;
				heartBeater.txtWorldTime.Text = e.WorldTime.ToString("ddd dd-MM-yyyy - HH'h' mm'm' ss's' 'GMT'K");
			}
			catch
			{
			}
		}

		private void ClientEmailReceived(object sender, EmailReceivedEventArgs e)
		{
			//display notification about new email in message window
			chatControl.ChatMessages.Add(new ChatMessageItem
			{
				MessageTimestamp = DateTime.Now.ToShortTimeString(),
				MessageSender = "Email System",
				MessageText = string.Format("Received new mail from: {0} on account: {1}", e.senderEmailAddress, e.serverName)
			});
			VisualStateManager.GoToState(this, "ChatExpanded", true);
		}

		private void Client_TraceEndedReceived(object sender, TraceEndedReceivedEventArgs e)
		{
			//TraceViewItem tvi = taskBar.StatusItems.Where(it => it.ConnectionId == e.ConnectionId).FirstOrDefault();
			//if (tvi != null)
			//    taskBar.StatusItems.Remove(tvi);
		}

		private void Client_TraceReceived(object sender, TraceReceivedEventArgs e)
		{
			//TraceViewItem tvi = taskBar.StatusItems.Where(it => it.ConnectionId == e.ConnectionId).FirstOrDefault();
			//if (tvi == null)
			//{
			//    tvi = new TraceViewItem { ConnectionId = e.ConnectionId, InitialTime = e.RemainingMilliSeconds, CurrentValue = e.RemainingMilliSeconds };
			//    taskBar.StatusItems.Add(tvi);
			//    return;
			//}
			//tvi.UpdateStatus(e.RemainingMilliSeconds);
		}

		private void chatControl_SpamDetected(object sender, EventArgs e)
		{
			MessageBox.Show(
				@"We have detected chat spamming. 
You will now be disconnected and temporarily banned for 30 minutes. 
Any subsequent violation will increase ban time up to permanent ban.

If you feel that this is incorrect, please contact support.");
			App.Client.LogoutAsync(App.InternalSessionId, App.Login, App.Password);
			App.RootControl.ContentBorder.Child = null;
		}

		private void chatControl_SendMessage(object sender, ChatControlMessageEventArgs e)
		{
			App.Client.SendGlobalChatMessageAsync(App.InternalSessionId, new ChatMessage {Message = e.Message});
		}

		private void ClientChatMessageReceived(object sender, GlobalChatMessageReceivedEventArgs e)
		{
			if (e.message.SystemId == 0) //only global messages get processed here
			{
				chatControl.ChatMessages.Add(new ChatMessageItem
				{
					MessageSender = "[" + e.message.Sender + "]",
					MessageText = e.message.Message,
					MessageTimestamp = e.message.Timestamp.ToShortTimeString()
				});
				VisualStateManager.GoToState(this, "ChatExpanded", true);
			}
		}

		private void Client_ServerDisconnectReceived(object sender, AsyncCompletedEventArgs e)
		{
			MessageBox.Show("You have been disconnected by the server. Please log in again.");
			App.RootControl.ContentBorder.Child = new LoginScreen();
			App.ResetClient();
			//App.Client.CloseCompleted += (_s, _e) => App.Client = null;
			//App.Client.CloseAsync();
		}

		private void InitializeStartMenu()
		{
			taskBar.StartMenuCloseButtonClicked += BtnQuitSessionClick;
			taskBar.MainButtonClicked += delegate { RefreshStartMenuPrograms(); };

			StartMenuProgramGroupItem showFileExplorer = new StartMenuProgramGroupItem();
			showFileExplorer.ProgramName = "Memory Banks";
			showFileExplorer.Click += ShowFileExplorerClick;
			taskBar.ClientFeatures.Add(showFileExplorer);

			StartMenuProgramGroupItem showHardware = new StartMenuProgramGroupItem();
			showHardware.ProgramName = "Hardware configuration";
			showHardware.Click += ShowHardwareClick;
			taskBar.ClientFeatures.Add(showHardware);

			StartMenuProgramGroupItem showEmptyWindow = new StartMenuProgramGroupItem();
			showEmptyWindow.ProgramName = "Usenet local test";
			showEmptyWindow.Click += delegate
			{
				FeatureUsenetControl usenet = new FeatureUsenetControl();
				usenet.Load(0);
				var wnd = _desktop.CreateWindow(showEmptyWindow.ProgramName, usenet);
				wnd.Width = 800;
				wnd.Height = 480;
				wnd.AllowMinimize = true;
				wnd.AllowMaximize = true;
				wnd.AllowClose = true;
				wnd.Show();
			};
			taskBar.ClientFeatures.Add(showEmptyWindow);
		}

		private void ShowHardwareClick(object sender, ProgramItemExecuteArgs e)
		{
			FeatureHardwareStorage hardware = new FeatureHardwareStorage();
			TheGameWindow hardwareWindow = _desktop.CreateWindow("Hardware configuration", hardware);
			hardwareWindow.AllowMinimize = true;
			hardwareWindow.AllowMaximize = true;
			hardwareWindow.SetSize(new Size(800, 600));
			hardwareWindow.Show();
			hardware.Load(App.MySystemId);
		}

		private void RefreshStartMenuPrograms()
		{
			App.Client.GetMyProgramsCompleted += ClientGetMyProgramsCompleted;
			App.Client.GetMyProgramsAsync(App.InternalSessionId);
		}

		private void ClientGetMyProgramsCompleted(object sender, GetMyProgramsCompletedEventArgs e)
		{
			App.Client.GetMyProgramsCompleted -= ClientGetMyProgramsCompleted;
			if (e.Error != null)
			{
				App.ServerError();
				return;
			}
			if (e.Result != null)
			{
				taskBar.StartMenuGroups.Clear();
				var categories = (from x in e.Result select x.Category).Distinct(new ProgramGroupComparer()).ToList();
				foreach (ProgramCategory cat in categories)
				{
					StartMenuProgramGroup programGroup; // = new StartMenuProgramGroup();
					if (cat == null) //happens if program is created without category. Shouldnt happen, but can.
					{
						programGroup = (from g in taskBar.StartMenuGroups where g.CategoryName == "Unsorted" select g).FirstOrDefault();
						if (programGroup == null)
						{
							programGroup = new StartMenuProgramGroup {CategoryName = "Unsorted", CategoryId = 0};
							taskBar.StartMenuGroups.Add(programGroup);
						}
					}
					else
					{
						programGroup = (from g in taskBar.StartMenuGroups where g.CategoryName == cat.Name select g).FirstOrDefault();
						if (programGroup == null)
						{
							programGroup = new StartMenuProgramGroup {CategoryName = cat.Name, CategoryId = cat.Id};
							taskBar.StartMenuGroups.Add(programGroup);
						}
					}
					IEnumerable<Program> programsInGroup;
					if (cat != null)
						programsInGroup = (from x in e.Result where x.Category != null && x.Category.Name == cat.Name select x);
					else
						programsInGroup = (from x in e.Result where x.Category == null select x);

					foreach (Program p in programsInGroup.OrderBy(it => it.Name))
					{
						if ((from i in programGroup.Items where i.ProgramId == p.Id select i).Count() > 0)
							continue;

						StartMenuProgramGroupItem item = new StartMenuProgramGroupItem();
						item.ProgramName = string.Format("{0} v{1}.0", p.Name, p.Version);
						item.InterfaceId = p.InterfaceId;
						item.ProgramId = p.Id;
						item.ProgramVersion = p.Version;
						item.Click += TaskBarStartMenuProgramExecuted;
						programGroup.Items.Add(item);
					}
				}
			}
		}

		private void BtnQuitSessionClick(object sender, EventArgs e)
		{
			if (
				MessageBox.Show("This will log you out of the current session. Are you sure?", "Warning", MessageBoxButton.OKCancel) ==
				MessageBoxResult.OK)
			{
				App.Client.LogoutCompleted += client_LogoutCompleted;
				App.Client.LogoutAsync(App.InternalSessionId, App.Login, App.Password);
			}
		}

		private void client_LogoutCompleted(object sender, LogoutCompletedEventArgs e)
		{
			App.Client.LogoutCompleted -= client_LogoutCompleted;
			if (e.Error != null) return;
			if (e.Result)
			{
				App.InternalSessionId = Guid.Empty;
				App.ResetClient();
				App.RootControl.ContentBorder.Child = new LoginScreen();
			}
			else
			{
				MessageBox.Show("Logout failed. (??)");
			}
		}

		#region local file manager activation

		private void ShowFileExplorerClick(object sender, ProgramItemExecuteArgs e)
		{
			GatewayFileManager mgr = new GatewayFileManager();
			mgr.OnRefreshRequested += (o, args) => RefreshGatewayFileManager(o);
			RefreshGatewayFileManager(mgr);
			TheGameWindow explorer = _desktop.CreateWindow("Memory Banks", mgr);
			explorer.AllowMinimize = true;
			explorer.AllowMaximize = true;
			explorer.SetSize(new Size(800, 480));
			explorer.Show();
		}

		private void RefreshGatewayFileManager(object sender)
		{
			//sender is always of type GatewayFileManager
			GatewayFileManager mgr = sender as GatewayFileManager;
			if (mgr.DirectoryId == 0)
				mgr.DirectoryId = App.MyRootDirectoryId;

			App.Client.GetMyTotalMemoryCompleted += Client_GetMyTotalMemoryCompleted;
			EventHandler<GetMyTotalMemoryCompletedEventArgs> clientOnGetMyTotalMemoryCompleted = null;
			clientOnGetMyTotalMemoryCompleted = (s, e) =>
			{
				App.Client.GetMyTotalMemoryCompleted -= clientOnGetMyTotalMemoryCompleted;
				if (e.Error != null)
				{
					App.ServerError();
					return;
				}
				if (e.Result < 0)
				{
					MessageBox.Show("Error occured during file explorer initialization.");
					return;
				}
				mgr.TotalCapacity = e.Result;
				
			};
			App.Client.GetMyTotalMemoryAsync(App.InternalSessionId);

			EventHandler<GetFilesInDirectoryCompletedEventArgs> clientOnGetFilesInDirectoryCompleted = null;
			clientOnGetFilesInDirectoryCompleted = (s, e) =>
			{
				App.Client.GetFilesInDirectoryCompleted -= clientOnGetFilesInDirectoryCompleted;
				if (e.Error != null)
				{
					App.ServerError();
					return;
				}
				if (e.Result != null)
				{
					mgr.FileItems.Clear();
					foreach (SystemFile f in e.Result)
					{
						GatewayFileItem fi = new GatewayFileItem
						{
							FileName = f.Name,
							FileSize = f.DisplaySize,
							FileId = f.Id,
							FileEncryptionLevel = f.EncryptionLevel,
							FileCompressionLevel = f.CompressionLevel,
							FileVersion = (f is ProgramFile) ? ((ProgramFile) f).Version : 0,
						};
						mgr.FileItems.Add(fi);
					}
					mgr.Refresh();
				}
			};
			App.Client.GetFilesInDirectoryCompleted += clientOnGetFilesInDirectoryCompleted;
			App.Client.GetFilesInDirectoryAsync(App.InternalSessionId, App.MyRootDirectoryId);
			//client.GetSystemRootAsync(App.InternalSessionId, App.MySystemId);
		}

		void Client_GetMyTotalMemoryCompleted(object sender, GetMyTotalMemoryCompletedEventArgs e)
		{
		}

		#endregion
	}

	public class ProgramGroupComparer : IEqualityComparer<ProgramCategory>
	{
		#region IEqualityComparer<ProgramCategory> Members

		public bool Equals(ProgramCategory x, ProgramCategory y)
		{
			if (x.Name == y.Name)
				return true;
			return false;
		}

		public int GetHashCode(ProgramCategory obj)
		{
			return obj.Name.GetHashCode();
		}

		#endregion
	}
}